using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

namespace CustomTween
{
    public enum EaseType
    {
        Linear,
        InQuad,
        OutQuad,
        InOutQuad,
        InCubic,
        OutCubic,
        InOutCubic
    }

    public class Tween
    {
        public static List<Tween> ActiveTweens = new List<Tween>();

        private MonoBehaviour coroutineHost;
        private Coroutine tweenCoroutine;
        private Transform targetTransform;
        private Vector3 startPosition;
        private Vector3 endPosition;
        private Vector3 startScale;
        private Vector3 endScale;
        private Quaternion startRotation;
        private Quaternion endRotation;
        private float duration;
        private float elapsedTime;
        private EaseType easeType;
        private Action onStart;
        private Action onUpdate;
        private Action onComplete;
        private bool isPlaying;
        private TweenType tweenType;

        private Action<Transform, float> customAction; // 自定义修改函数

        [Flags]
        private enum TweenType
        {
            None = 0,
            Move = 1,
            Scale = 2,
            Rotate = 4,
            Custom = 8,
        }

        private Tween(Transform target, float duration, EaseType easeType = EaseType.Linear)
        {
            this.targetTransform = target;
            this.duration = duration;
            this.easeType = easeType;
            this.elapsedTime = 0f;
            this.isPlaying = false;
            this.tweenType = TweenType.None;
        }

        public static Tween MoveTo(Transform target, Vector3 endPosition, float duration, EaseType easeType = EaseType.Linear)
        {
            Tween tween = new Tween(target, duration, easeType);
            tween.startPosition = target.position;
            tween.endPosition = endPosition;
            tween.tweenType |= TweenType.Move;
            return tween;
        }

        public static Tween ScaleTo(Transform target, Vector3 endScale, float duration, EaseType easeType = EaseType.Linear)
        {
            Tween tween = new Tween(target, duration, easeType);
            tween.startScale = target.localScale;
            tween.endScale = endScale;
            tween.tweenType |= TweenType.Scale;
            return tween;
        }

        public static Tween RotateTo(Transform target, Quaternion endRotation, float duration, EaseType easeType = EaseType.Linear)
        {
            Tween tween = new Tween(target, duration, easeType);
            tween.startRotation = target.rotation;
            tween.endRotation = endRotation;
            tween.tweenType |= TweenType.Rotate;
            return tween;
        }

        public static Tween Custom(Transform target, Action<Transform, float> customAction, float duration, EaseType easeType = EaseType.Linear)
        {
            Tween tween = new Tween(target, duration, easeType);
            tween.customAction = customAction;
            tween.tweenType |= TweenType.Custom;
            return tween;
        }

        public Tween OnStart(Action callback)
        {
            this.onStart += callback;
            return this;
        }

        public Tween OnUpdate(Action callback)
        {
            this.onUpdate += callback;
            return this;
        }

        public Tween OnComplete(Action callback)
        {
            this.onComplete += callback;
            return this;
        }

        public Tween SetEase(EaseType ease)
        {
            this.easeType = ease;
            return this;
        }

        public void Play(MonoBehaviour host)
        {
            if (isPlaying) return;
            
            coroutineHost = host;
            isPlaying = true;
            ActiveTweens.Add(this);
            tweenCoroutine = host.StartCoroutine(TweenCoroutine());
        }

        public void Stop()
        {
            if (!isPlaying) return;
            
            if (coroutineHost != null && tweenCoroutine != null)
            {
                coroutineHost.StopCoroutine(tweenCoroutine);
            }
            
            isPlaying = false;
            ActiveTweens.Remove(this);
        }

        private IEnumerator TweenCoroutine()
        {
            onStart?.Invoke();
            elapsedTime = 0f;

            while (elapsedTime < duration)
            {
                elapsedTime += Time.deltaTime;
                float t = Mathf.Clamp01(elapsedTime / duration);
                float easedT = ApplyEase(t, easeType);

                if ((tweenType & TweenType.Move) != 0)
                {
                    targetTransform.position = Vector3.Lerp(startPosition, endPosition, easedT);
                }

                if ((tweenType & TweenType.Scale) != 0)
                {
                    targetTransform.localScale = Vector3.Lerp(startScale, endScale, easedT);
                }

                if ((tweenType & TweenType.Rotate) != 0)
                {
                    targetTransform.rotation = Quaternion.Lerp(startRotation, endRotation, easedT);
                }

                if ((tweenType & TweenType.Custom) != 0 && customAction != null)
                {
                    customAction.Invoke(targetTransform, easedT);
                }

                onUpdate?.Invoke();
                yield return null;
            }

            // Ensure we end exactly at the target values
            if ((tweenType & TweenType.Move) != 0)
            {
                targetTransform.position = endPosition;
            }

            if ((tweenType & TweenType.Scale) != 0)
            {
                targetTransform.localScale = endScale;
            }

            if ((tweenType & TweenType.Rotate) != 0)
            {
                targetTransform.rotation = endRotation;
            }

            if ((tweenType & TweenType.Custom) != 0 && customAction != null)
            {
                customAction.Invoke(targetTransform, 1f);
            }

            onComplete?.Invoke();
            isPlaying = false;
            ActiveTweens.Remove(this);
        }

        private float ApplyEase(float t, EaseType ease)
        {
            switch (ease)
            {
                case EaseType.Linear:
                    return t;
                case EaseType.InQuad:
                    return t * t;
                case EaseType.OutQuad:
                    return t * (2f - t);
                case EaseType.InOutQuad:
                    if (t < 0.5f) return 2f * t * t;
                    return -1f + (4f - 2f * t) * t;
                case EaseType.InCubic:
                    return t * t * t;
                case EaseType.OutCubic:
                    return 1f + (--t) * t * t;
                case EaseType.InOutCubic:
                    if (t < 0.5f) return 4f * t * t * t;
                    return (t - 1f) * (2f * t - 2f) * (2f * t - 2f) + 1f;
                default:
                    return t;
            }
        }
    }

    // Example usage component
    public class TweenExample : MonoBehaviour
    {
        public Transform targetObject;
        public Vector3 moveToPosition = new Vector3(5, 0, 0);
        public Vector3 scaleToSize = new Vector3(2, 2, 2);
        public Vector3 rotateToAngle = new Vector3(0, 180, 0);
        public float duration = 2f;
        public EaseType easeType = EaseType.OutQuad;

        [Header("Custom Callback Parameters")]
        public string customMessage = "Tween Complete!";
        public int customValue = 42;

        void Start()
        {
            // Example of chaining tweens with custom callbacks
            Tween.MoveTo(targetObject, moveToPosition, duration, easeType)
                .OnStart(() => Debug.Log("Move started"))
                .OnUpdate(() => Debug.Log("Moving..."))
                .OnComplete(() => {
                    Debug.Log("Move completed");
                    // Start scale tween after move completes
                    Tween.ScaleTo(targetObject, scaleToSize, duration, easeType)
                        .OnComplete(() => {
                            Debug.Log("Scale completed");
                            // Start rotation tween after scale completes
                            Tween.RotateTo(targetObject, Quaternion.Euler(rotateToAngle), duration, easeType)
                                .OnComplete(() => CustomCallback(customMessage, customValue))
                                .Play(this);
                        })
                        .Play(this);
                })
                .Play(this);
        }

        // Custom callback method with parameters
        private void CustomCallback(string message, int value)
        {
            Debug.Log($"{message} The answer is {value}");
            // You can add any custom logic here
        }

        // Example of using lambda expressions for custom callbacks
        public void StartComplexTween()
        {
            Tween.MoveTo(targetObject, moveToPosition, duration, easeType)
                .OnStart(() => {
                    Debug.Log("Starting complex tween");
                    targetObject.GetComponent<Renderer>().material.color = Color.red;
                })
                .OnUpdate(() => {
                    float progress = Vector3.Distance(targetObject.position, moveToPosition) / Vector3.Distance(transform.position, moveToPosition);
                    Debug.Log($"Progress: {progress:P0}");
                })
                .OnComplete(() => {
                    Debug.Log("Complex tween completed");
                    targetObject.GetComponent<Renderer>().material.color = Color.green;
                    // You can call any method with any parameters here
                    SomeMethod(42, "Hello", targetObject.gameObject);
                })
                .Play(this);
        }

        private void SomeMethod(int number, string text, GameObject obj)
        {
            Debug.Log($"Number: {number}, Text: {text}, Object: {obj.name}");
        }
    }
}